package com.xx.doublexchat.common.user.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.xx.doublexchat.common.chat.domain.entity.RoomFriend;
import com.xx.doublexchat.common.chat.service.ChatService;
import com.xx.doublexchat.common.chat.service.IRoomService;
import com.xx.doublexchat.common.chat.service.adapter.MessageAdapter;
import com.xx.doublexchat.common.common.annotation.RedissonLock;
import com.xx.doublexchat.common.common.domain.vo.request.CursorPageBaseRequest;
import com.xx.doublexchat.common.common.domain.vo.request.PageBaseRequest;
import com.xx.doublexchat.common.common.domain.vo.response.CursorPageBaseResponse;
import com.xx.doublexchat.common.common.domain.vo.response.PageBaseResponse;
import com.xx.doublexchat.common.common.utils.AssertUtils;
import com.xx.doublexchat.common.user.adapter.FriendAdapter;
import com.xx.doublexchat.common.user.dao.UserApplyDao;
import com.xx.doublexchat.common.user.dao.UserDao;
import com.xx.doublexchat.common.user.dao.UserFriendDao;
import com.xx.doublexchat.common.user.domain.entity.FriendApplyResponse;
import com.xx.doublexchat.common.user.domain.entity.User;
import com.xx.doublexchat.common.user.domain.entity.UserApply;
import com.xx.doublexchat.common.user.domain.entity.UserFriend;
import com.xx.doublexchat.common.user.domain.enums.ApplyStatusEnum;
import com.xx.doublexchat.common.user.domain.vo.request.FriendApplyRequest;
import com.xx.doublexchat.common.user.domain.vo.request.FriendApproveRequest;
import com.xx.doublexchat.common.user.domain.vo.request.FriendCheckRequest;
import com.xx.doublexchat.common.user.domain.vo.response.FriendCheckResponse;
import com.xx.doublexchat.common.user.domain.vo.response.FriendResponse;
import com.xx.doublexchat.common.user.domain.vo.response.FriendUnreadResponse;
import com.xx.doublexchat.common.user.event.UserApplyEvent;
import com.xx.doublexchat.common.user.service.IUserFriendService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
public class UserFriendServiceimpl implements IUserFriendService {

    @Autowired
    private UserFriendDao userFriendDao;

    @Autowired
    private UserDao userDao;

    @Autowired
    private UserApplyDao userApplyDao;

    @Autowired
    private IRoomService roomService;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    private ChatService chatService;

    /**
     * 联系人列表
     *
     * @param uid
     * @param request
     * @return
     */
    @Override
    public CursorPageBaseResponse<FriendResponse> friendList(Long uid, CursorPageBaseRequest request) {
        //游标获取当前好友信息
        CursorPageBaseResponse<UserFriend> friendPage = userFriendDao.getFriendPage(uid, request);
        if (Objects.nonNull(friendPage) && CollectionUtils.isEmpty(friendPage.getList())) {
            return CursorPageBaseResponse.empty();
        }

        //从游标信息中获取当前联系人的id
        List<Long> friendUids = friendPage.getList()
                .stream().map(UserFriend::getFriendUid)
                .collect(Collectors.toList());

        List<User> userList = userDao.getFriendList(friendUids);
        return CursorPageBaseResponse.init(friendPage, FriendAdapter.buildFriend(friendPage.getList(), userList));
    }

    /**
     * 检查是否为自己的好友
     *
     * @param uid
     * @param request
     * @return
     */
    @Override
    public FriendCheckResponse check(Long uid, FriendCheckRequest request) {
        //获取所有好友信息
        List<UserFriend> friendList = userFriendDao.getByFriends(uid, request.getUidList());
        //获取所有好友的id
        Set<Long> friendUidList = friendList.stream().map(UserFriend::getFriendUid).collect(Collectors.toSet());
        List<FriendCheckResponse.FriendCheck> friendCheckList = request.getUidList().stream().map(friendUid -> {
            //判断传入的id是否为该用户的好友
            FriendCheckResponse.FriendCheck friendCheck = new FriendCheckResponse.FriendCheck();
            friendCheck.setUid(friendUid);
            friendCheck.setIsFriend(friendUidList.contains(friendUid));
            return friendCheck;
        }).collect(Collectors.toList());
        return new FriendCheckResponse(friendCheckList);
    }

    /**
     * 申请好友
     *
     * @param uid
     * @param request
     */
    @Override
    @RedissonLock(key = "#uid")
    public void apply(Long uid, FriendApplyRequest request) {
        //是否有好友关系
        AssertUtils.isTrue(userFriendDao.validateFriend(uid, request.getTargetUid()), "你们已经是好友了");
        //是否有待审批的申请记录(自己的)
        if (userApplyDao.validateApplySelf(uid, request.getTargetUid())) {
            log.info("已有好友申请记录,uid:{}, targetId:{}", uid, request.getTargetUid());
            return;
        }
        //是否有待审批的申请记录(别人请求自己的)
        if (userApplyDao.validateApplySelf(request.getTargetUid(), uid)) {
            UserApply friendApproving = userApplyDao.getFriendApproving(request.getTargetUid(), uid);
            //申请通过 ---> 同意好友申请  ---> 动态代理保证事务生效
            ((IUserFriendService) AopContext.currentProxy()).applyApprove(uid, new FriendApproveRequest(friendApproving.getId()));
            return;
        }
        // 申请入库
        UserApply insert = FriendAdapter.buildFriendApply(uid, request);
        userApplyDao.save(insert);
        // 申请事件
        applicationEventPublisher.publishEvent(new UserApplyEvent(this, insert));
    }

    /**
     * 同意好友申请
     *
     * @param uid
     * @param request
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @RedissonLock(key = "#uid")
    public void applyApprove(Long uid, FriendApproveRequest request) {
        // 获取好友申请信息
        UserApply userApply = userApplyDao.getById(request.getApplyId());
        AssertUtils.isNotEmpty(userApply, "不存在申请记录");
        AssertUtils.equal(userApply.getTargetId(), uid, "不存在申请记录");
        AssertUtils.equal(userApply.getStatus(), ApplyStatusEnum.WAIT_APPROVAL.getCode(), "已同意好友申请");
        //同意申请
        userApplyDao.agree(request.getApplyId());
        //创建双方好友关系
        createFriend(uid, userApply.getUid());
        // 创建一个聊天房间
        RoomFriend roomFriend = roomService.createFriendRoom(Arrays.asList(uid, userApply.getUid()));
        // 发送一条同意消息。。我们已经是好友了，开始聊天吧
        chatService.sendMsg(MessageAdapter.buildAgreeMsg(roomFriend.getRoomId()), uid);
    }

    /**
     * 好友申请列表
     * @param uid
     * @param request
     * @return
     */
    @Override
    public PageBaseResponse<FriendApplyResponse> pageApplyFriend(Long uid, PageBaseRequest request) {
        //获取所有好友申请信息--->并按照创建时间降序排序
        IPage<UserApply> userApplyIPage = userApplyDao.friendApplyPage(uid, request.plusPage());
        if (CollectionUtil.isEmpty(userApplyIPage.getRecords())) {
            return PageBaseResponse.empty();
        }
        //只要请求好友申请列表就表明已经阅读了，需要更新状态
        readApples(uid, userApplyIPage);
        //封装返回体
        return PageBaseResponse.init(userApplyIPage, FriendAdapter.buildFriendApplyList(userApplyIPage.getRecords()));
    }

    /**
     * 申请好友未读数
     * @param uid
     * @return
     */
    @Override
    public FriendUnreadResponse unread(Long uid) {
        Integer unReadCount = userApplyDao.getUnReadCount(uid);
        return new FriendUnreadResponse(unReadCount);
    }

    @Override
    public void deleteFriend(Long uid, Long friendUid) {
        List<UserFriend> userFriends = userFriendDao.getUserFriend(uid, friendUid);
        if (CollectionUtil.isEmpty(userFriends)) {
            log.info("没有好友关系：{},{}", uid, friendUid);
            return;
        }
        List<Long> friendRecordIds = userFriends.stream().map(UserFriend::getId).collect(Collectors.toList());
        userFriendDao.removeByIds(friendRecordIds);
        // 禁用房间
        roomService.disableFriendRoom(Arrays.asList(uid, friendUid));
    }

    /**
     * 回写阅读状态
     * @param uid
     * @param userApplyIPage
     */
    private void readApples(Long uid, IPage<UserApply> userApplyIPage) {
        List<Long> applyIdList = userApplyIPage.getRecords()
                .stream().map(UserApply::getId)
                .collect(Collectors.toList());
        userApplyDao.readApples(uid,applyIdList);
    }

    /**
     * 创建好友关系
     *
     * @param uid
     * @param friendId
     */
    private void createFriend(Long uid, Long friendId) {
        UserFriend self = new UserFriend();
        self.setUid(uid);
        self.setFriendUid(friendId);
        UserFriend friend = new UserFriend();
        friend.setFriendUid(uid);
        friend.setUid(friendId);
        List<UserFriend> updateList = new ArrayList<>();
        updateList.add(self);
        updateList.add(friend);
        userFriendDao.saveBatch(updateList);
    }

}
